/*
 * @Description: 渲染布局计算方法（改进）
 * @version: 0.1
 * @Author: E-CHOOSEUS R&D
 * @LastEditors: E-CHOOSEUS R&D
 */

import { pageBaseInfo } from '../layout-compute'

/**
 * @description 查找树的每一层最大高度
 * @param {*} nodeList 子节点列表
 * @param {*} topHeight 因根节点为空, 所以该参数默认传递 0
 * @returns
 */
const getTreeLayerHeight = (nodeList = [], topHeight = 0) => {
  const rootTree = {
    rowHeight: 0,
    isRoot: false,
    // layers: 1, // 树的层级
    columns: [] // 该层节点列表
  }

  const nodesHeight = [] // * 当前节点的子节点列表高度
  for (const node of nodeList) {
    if (node.isLeaf) {
      nodesHeight.push(node.height)
      rootTree.columns.push({
        rowHeight: node.height,
        ...node,
        // layers: 1,
        columns: []
      })
      continue
    }

    const computedNode = getTreeLayerHeight(node.childList, node.height)
    nodesHeight.push(computedNode.rowHeight)
    rootTree.columns.push(Object.assign(computedNode, node, { childList: [] })) // *
  }

  // 当前节点子节点高度取最大 + 当前节点高度
  rootTree.rowHeight = Math.max(...nodesHeight) + topHeight

  return rootTree
}

/**
 * @description 平分树总高大于子树高度的空间到每一层
 * @param {*} nodeList 节点列表
 * @param {*} totalHeight 树总高
 * @param {*} subTreeHeight 子树总高
 * @param {*} topSubTree 是否根节点子树总高， 正常来说, 第三个参数应该为true, 但由于根节点为空, 所以初次调用时应为 false
 * @param {*} level 用于分配剩余高度的层级
 * @returns
 */
const averageTreeOverHeight = (
  nodeList,
  totalHeight,
  subTreeHeight,
  topSubTree = true,
  level = 1
) => {
  let unassignedH = totalHeight - subTreeHeight // 未分配的高度
  for (const node of nodeList) {
    subTreeHeight = topSubTree ? subTreeHeight : node.rowHeight
    const overHeight = totalHeight - subTreeHeight
    let leftHeigh = overHeight
    // ? overHeight 为0是一个分界点, 代表该子树为最高, 但不代表子子树都等高
    if (overHeight === 0) {
      leftHeigh = averageTreeOverHeight(
        node.columns,
        totalHeight - node.height,
        0,
        false
      )
    } else if (overHeight > 0) {
      if (!node.isLeaf && node.columns) {
        leftHeigh = averageTreeOverHeight(
          node.columns,
          totalHeight,
          subTreeHeight,
          true,
          level + 1
        )
      }
    } else {
      // ! 不应该出现这种情况: overHeight < 0
    }
    node.renderH = node.height + leftHeigh / level // 只有一层时
    node.renderRowH =
      node.rowHeight + (overHeight - leftHeigh) + leftHeigh / level // 新的行高

    unassignedH = leftHeigh - leftHeigh / level
  }

  return unassignedH // 返回 0 分配高度
}

// * 表头列高度分配策略, 用于解决 层级数较少的列和整个表头高度之间的富余空间分配
export const TitleColumnsHeightStrategy = {
  top: 'top', // 赋予第一层
  average: 'average', // 平分
  bottom: 'bottom' // 赋予最后一层
}

/**
 * @summary 测试状态: 已测试, 通过
 * @description 表头列中不足最大高度的子树, 将不足的高度分配到直接子节点
 * @param {*} nodeList 表头列表
 * @param {*} treeMaxH 该层最大宽度
 */
const dispatchOverHeightToTop = (nodeList, treeMaxH) => {
  for (const node of nodeList) {
    if (node.isLeaf) {
      node.renderH = treeMaxH
    } else {
      // 渲染高度 = 节点高度 + 多余高度（层级最大高度 - 节点(树)高）
      node.renderH = node.height + treeMaxH - node.rowHeight
      const subNodeMaxH = treeMaxH - node.renderH

      const childList = node.columns || []
      dispatchOverHeightToTop(childList, subNodeMaxH)
    }
    node.renderRowH = treeMaxH
  }
}

/**
 * @summary 测试状态: 已测试, 通过
 * @description 表头列中不足最大高度的子树, 将不足的高度分配到叶子节点
 * @param {*} nodeList 表头列表
 * @param {*} treeMaxH 该层最大高度
 * @returns 子树列表调整后的最大高度
 */
const dispatchOverHeightToBottom = (nodeList, treeMaxH) => {
  const nodeHeightList = []
  for (const node of nodeList) {
    if (node.isLeaf) {
      node.renderH = treeMaxH
      node.renderRowH = node.renderH + 0 // node Tree 的最大高度 = 新的渲染高度 + 子节点最大高度
      nodeHeightList.push(node.renderRowH)
      // continue;
    } else {
      const subNodeMaxH = treeMaxH - node.height
      const childList = node.columns || []
      const subNodeH = dispatchOverHeightToBottom(childList, subNodeMaxH)

      node.renderH = node.height
      node.renderRowH = node.renderH + subNodeH // node Tree 的最大高度 = 新的渲染高度 + 子节点最大高度
      nodeHeightList.push(node.renderRowH)
    }
  }

  return Math.max(...nodeHeightList)
}

/**
 * TODO 优化表格的渲染数据
 * @description 优化表格头部非最高子树对剩余高度的分配
 * @param {*} columns
 */
export function optimizeTitleColumns(
  columns,
  strategy = TitleColumnsHeightStrategy.top
) {
  // 表头树, 经过考虑, 采用对象的方式来保存比较合理
  // const titleTree = {
  //   rowHeight: 0, // 总行高
  //   columns: [] // 节点列表
  // };

  //  TODO 第一步， 找出每一层的最大高度
  const titleTree = getTreeLayerHeight(columns, 0)
  titleTree.isRoot = true // * 根节点做特别标识

  // console.log("titleTree: ", titleTree);
  // const treeHeight = titleTree.rowHeight; // 表头高度

  switch (strategy) {
    // 表头剩余高度平分
    case TitleColumnsHeightStrategy.average:
      // ? 将根节点看成一个高为0的虚拟节点
      averageTreeOverHeight(
        titleTree.columns,
        titleTree.rowHeight - 0,
        0,
        false
      )
      break
    case TitleColumnsHeightStrategy.top:
      dispatchOverHeightToTop(titleTree.columns, titleTree.rowHeight)
      break
    // 表头剩余高度分配到叶子节点
    case TitleColumnsHeightStrategy.bottom:
      dispatchOverHeightToBottom(titleTree.columns, titleTree.rowHeight)
      break
    default:
      break
  }

  return titleTree
}

/**
 * @description 收集表头配置项DOM渲染数据
 * @param {*} theadDom
 * @returns
 */
export function getRenderedTitleColumns(theadDom) {
  const titleColumns = []

  if (theadDom) {
    /**
     * @summary 仅针对 preRenderTitle.vue 组件的DOM结构
     * @description 递归获取表头节点渲染信息
     * @param children 节点列表
     * @param level 节点层级
     * @returns 返回预渲染节点的渲染信息树
     */
    const getColumnsTree = (children = [], level) => {
      const list = []
      for (let i = 0, len = children.length; i < len; ++i) {
        const l1Node = children[i] // 一级大类
        const column = {
          _uuid: '',
          text: '', // 节点文字
          width: 'auto',
          height: 'auto',
          level: level, // 节点层级
          isLeaf: false, // 是否叶子节点
          childList: [] // 子节点列表
        }
        const l1SubChildNodes = l1Node.children // 大类下的子项列表

        if (l1SubChildNodes.length) {
          const titleNode = l1SubChildNodes[0] // 第一个是标题节点
          column._uuid = titleNode.dataset.id
          column.text = titleNode.innerText
          // column.width = titleNode.offsetWidth;
          column.width = titleNode.dataset.width
            ? Math.max(titleNode.dataset.width * 1, titleNode.offsetWidth)
            : titleNode.offsetWidth
          column.height = titleNode.offsetHeight
          column.isLeaf = l1SubChildNodes.length === 1 // 没有第二个子节点, 则该节点为叶子节点
          if (l1SubChildNodes.length > 1) {
            const subTable = l1SubChildNodes[1] // 第二个是子级节点的父DOM元素, 等价于 preRenderTable
            subTable &&
              (column.childList = getColumnsTree(subTable.children, level + 1))
          }

          list.push(column)
        }
      }
      return list
    }

    const l1ChildNodes = theadDom.children // 全部一级大类节点列表
    const columns = getColumnsTree(l1ChildNodes, 1)
    titleColumns.splice(0, 0, ...columns)
  }

  return titleColumns
}

/**
 * @description 更新树节点中的布局数据
 * @param {*} treeColumns
 * @param {*} treeMap
 */
export function updateTitleColumnsLayoutData(tree, treeMap) {
  // console.log("updateTitleColumnsLayoutData tree: ", tree);
  const walkTree = (columns, upWidthList = []) => {
    for (const column of columns) {
      const widthList = []
      if (column && column._uuid) {
        const node = treeMap.get(column._uuid)
        node._level = column.level
        node._width = column.width
        node._height = column.renderH

        widthList.push(column.width)
        if (node._isLeaf) {
          const widthToTop = upWidthList.concat(...widthList)
          node._dTreeMaxW = Math.max(...widthToTop)
        }
      }
      if (column.columns) {
        walkTree(column.columns, upWidthList.concat(...widthList))
      }
    }
  }

  walkTree(tree.columns)

  /**
   * TODO 已整合的方法, 暂时无用, 可删除
   * @description 计算叶子节点到根节点树的最大宽度, 已整合到 walkTree方法
   * @param {*} columns
   * @param {*} upWidthList
   */
  const calcLeafMaxWidth = (columns, upWidthList = []) => {
    for (const column of columns) {
      const widthList = []
      if (column && column._uuid) {
        const node = treeMap.get(column._uuid)
        widthList.push(column.width)

        if (node._isLeaf) {
          const widthToTop = upWidthList.concat(...widthList)
          node._dTreeMaxW = Math.max(...widthToTop)
        }
      }
      if (column.columns) {
        calcLeafMaxWidth(column.columns, upWidthList.concat(...widthList))
      }
    }
  }
}

/**
 * @description 分页头部宽度优化
 * @param {*} pagesColumn
 */
export function optimizePagesColumnWidth(pagesColumn, pageInfo = pageBaseInfo) {
  /**
   * @description 获取一棵树每一层的宽度, 对于下文的宽度修正算法，该算法为计算依赖前提
   * @param {*} node
   * @param {*} layerWidthList
   * @returns
   */
  const getTreeLayerWidth = (node, layerWidthList = []) => {
    const rootTree = {
      layerWidth: 0,
      layerColumns: []
    }
    Object.assign(rootTree, node, {
      _childList: [],
      childNode: [],
      jgtChild: []
    })
    if (node._isLeaf) {
      rootTree.layerWidth = node._width
      rootTree.subLayerWidth = 0
      rootTree.subNodesWidth = 0
      return rootTree
    }
    let childWidth = 0 // 子节点层级宽度
    let childNodeWidth = 0 // 子节点列表宽度
    for (const childNode of node._childList) {
      const childTree = getTreeLayerWidth(childNode)
      childWidth += childTree.layerWidth
      childNodeWidth += childTree._width

      rootTree.layerWidth = Math.max(node._width, childWidth) // ? 与高度不同, 宽度最大的节点决定了整棵树的宽度
      rootTree.subLayerWidth = childWidth
      rootTree.subNodesWidth = childNodeWidth
      rootTree.layerColumns.push(childTree)
    }

    return rootTree
  }

  /**
   * ! 该算法有误, 应移除, 下文的 fillMissedWidth 算法为真正修正算法
   * todo 算法思路改进
   * @summary 该算法未通过完全测试, 可能会有潜在bug
   * @description 填充子节点不足宽度
   * @param {*} node
   * @param {*} targetWidth
   * @param {*} margin
   * @param {*} keysTree
   * @param {*} curRowWidth
   * @param {*} len
   * @returns
   */
  const fillTreeMissingWidth = (
    node,
    targetWidth,
    margin = 0,
    keysTree = {},
    curRowWidth,
    len
  ) => {
    node._fillWidth = node._width + (curRowWidth > 0 ? curRowWidth : 0) / len
    node._adjusted = false
    keysTree[node._uuid] = {
      layerWidth: node.layerWidth,
      subLayerWidth: node.subLayerWidth,
      subNodesWidth: node.subNodesWidth,
      _fillWidth: node._fillWidth,
      _adjusted: node._adjusted
    }

    const childList = node.layerColumns || []
    if (node._isLeaf || !childList.length) {
      const addWidth = (curRowWidth > 0 ? curRowWidth : 0) / len
      node._fillWidth = node._width + (curRowWidth > 0 ? curRowWidth : 0) / len
      node._adjusted = addWidth > 0

      keysTree[node._uuid]._fillWidth = node._fillWidth
      keysTree[node._uuid]._adjusted = node._adjusted
      return
    }

    const cLength = childList.length
    let diffWidth = 0
    for (const child of childList) {
      // 子节点的目标宽度 = 目标宽度 - 下层其他子节点的最大宽度
      const nextTargetWidth =
        targetWidth - (node.subLayerWidth - child.subLayerWidth)
      // 应该分配的宽度 = 子节点目标宽度 - 子节点自身宽度
      const shouldFillWidth = nextTargetWidth - child._width //

      diffWidth = targetWidth - node.subLayerWidth + child.subLayerWidth
      fillTreeMissingWidth(
        child,
        diffWidth,
        0,
        keysTree,
        nextTargetWidth,
        cLength
      )
    }

    return
  }

  /**
   * @summary 测试状态: 已测试, 测试通过
   * @description 节点宽度修正算法, 该算法为正确且实际可用算法
   * @param {*} node 当前节点, 也可理解点为树
   * @param {*} nodeTreeMaxW 节点树最大宽度
   * @param {*} assignWidth 需要补齐的宽度
   * @param {*} keysTree 信息记录对象
   */
  const fillMissedWidth = (node, nodeTreeMaxW, assignWidth, keysTree = {}) => {
    // 理论上: nodeTreeMaxW = node._width + assignWidth
    // console.log(
    //   "nodeTreeMaxW, assignWidth, node._width, nodeTreeMaxW === assignWidth + node._width:",
    //   nodeTreeMaxW,
    //   assignWidth,
    //   node._width,
    //   nodeTreeMaxW === assignWidth + node._width
    // );
    node._newWidth = node._width + assignWidth
    node._fillWidth = node._newWidth

    if (keysTree[node._uuid]) {
      keysTree[node._uuid]._fillWidth = node._fillWidth
      keysTree[node._uuid]._adjusted = node._isLeaf && assignWidth > 0
    }

    const childList = node.layerColumns || []

    // 父节点超过子节点宽度
    const overWidth = (node._newWidth - node.subLayerWidth) / childList.length // 分子最小应该为0
    for (const child of childList) {
      // const childAssignWidth = ;
      // 子节点实际渲染最大宽度
      const childTreeMaxW = child.layerWidth + overWidth
      fillMissedWidth(
        child,
        childTreeMaxW,
        childTreeMaxW - child._width,
        keysTree
      )
    }
  }

  /**
   * @description 遍历树, 并将关键信息附加到节点上
   * @param {*} node
   * @param {*} keysTree
   * @returns
   */
  const walkTree = (node, keysTree) => {
    if (node) {
      Object.assign(node, keysTree[node._uuid])

      const childList = node._childList || []
      if (node._isLeaf || !childList.length) {
        return
      }
      for (const child of childList) {
        walkTree(child, keysTree)
      }
    }
  }

  const pagesStretchW = []
  const resolvePageColumnsWExtend = (
    fixedWidthList = [],
    isLastPage = false,
    pageStretch = { w: 0 },
    pageColumns = []
  ) => {
    // if (!isLastPage) {
    //   for (const _ of fixedWidthList) {
    //     // console.log("resolvePageColumnsWExtend, _: ", _);
    //     const keys = Object.keys(_)
    //     for (const key of keys) {
    //       _[key]._stretchW = 0
    //     }
    //   }
    //   return
    // }
    let columnsWidth = 0
    let averageCount = 0 // 分配节点 + 1
    for (const _ of fixedWidthList) {
      const keys = Object.keys(_)
      for (const key of keys) {
        const fixItem = _[key]
        if (!fixItem.subNodesWidth) {
          columnsWidth += fixItem._fillWidth
          ++averageCount
        }
        fixItem._stretchW = 0
      }
    }

    const paperW = Math.floor(pageInfo.available.w)
    if (paperW - columnsWidth > 0) {
      const notComputeKeys = ['yx-bqgc', 'date_value']
      const notComputeItem = pageColumns.filter(v =>
        notComputeKeys.includes(v.dictValue)
      )
      const cutTimeCount = averageCount - notComputeItem.length // 不纳入特定列的计算
      const overW = paperW - columnsWidth // 拓展宽度
      const averageW = Math.floor(overW / cutTimeCount) // 平分后取整宽度
      const averageLeftW = overW % cutTimeCount // 取余后剩余宽度

      pageStretch.w += overW
      for (let i = 0, len = fixedWidthList.length; i < len; ++i) {
        const _ = fixedWidthList[i]
        const keys = Object.keys(_)
        for (const key of keys) {
          const fixItem = _[key]

          if (!fixItem.subNodesWidth) {
            if (
              averageCount - cutTimeCount !== 1 &&
              !notComputeItem.some(v => v._uuid === key)
            ) {
              fixItem._stretchW += averageW
            }
            --averageCount
            if (!averageCount) {
              fixItem._stretchW += averageLeftW
            }
          }
        }
      }
    }
  }

  let i = 0
  const pagesFixedWidth = []
  for (const pageColumns of pagesColumn) {
    const fixedWidth = []
    // console.log("--------------------------------------------", i);
    for (const column of pageColumns) {
      const columnWidthTree = getTreeLayerWidth(column)
      const treeWidth = columnWidthTree.layerWidth // * 树的宽度

      const keysTree = {}
      fillTreeMissingWidth(
        columnWidthTree,
        treeWidth,
        0,
        keysTree,
        treeWidth - columnWidthTree._width,
        1
      )
      fillMissedWidth(
        columnWidthTree,
        treeWidth,
        treeWidth - columnWidthTree._width,
        keysTree
      )

      walkTree(column, keysTree)
      fixedWidth.push(keysTree)
    }
    const pageStretch = {
      w: 0
    }
    resolvePageColumnsWExtend(
      fixedWidth,
      i + 1 === pagesColumn.length,
      pageStretch,
      pageColumns
    )
    pagesStretchW.push(pageStretch)
    pagesFixedWidth.push(fixedWidth)

    ++i
  }

  // console.log("pagesFixedWidth, pagesStretchW: ", pagesFixedWidth, pagesStretchW);

  return { pagesFixedWidth, pagesStretchW }
}
